/*
 *  Copyright (C) 2008      Tri Tech Information Systems Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "binder.h"
#include "codemodel.h"
#include "codemodel_pointer.h"
#include "control.h"
#include "lexer.h"
#include "parser.h"
#include "rpp/pp.h"

#include "pyQHash.h"
#include "pyQList.h"

#include <boost/python.hpp>
using namespace boost::python;

template< class T >
T* get_pointer( CodeModelPointer<T> const& p )
{
    return const_cast< T* >( p.data() );
}

namespace boost { namespace python
{
    template<class T>
    struct pointee< CodeModelPointer<T> >
    {
        typedef typename CodeModelPointer<T>::Type type;
    };
} }

static FileModelItem CodeModel_parseFile( CodeModel& model,
        std::string file, list include_paths )
{
    rpp::pp_environment env;
    rpp::pp proc( env );

    int l = len(include_paths);
    for(int idx = 0; idx < l; idx++)
        proc.push_include_path( extract<std::string>(include_paths[idx]) );

    std::string result;
    result.reserve( 20480 );
    proc.file( file, std::back_inserter(result) );

    pool mempool;
    Control c;
    Parser p( &c );
    TranslationUnitAST* ast = p.parse( result.c_str(), result.size(), &mempool );

    Binder b( &model, p.location(), &c );
    FileModelItem fm = b.run( ast );

    // Fix up the global namespace member...
    NamespaceModelItem globals = model.globalNamespace();
    foreach( NamespaceModelItem ns, fm->namespaces() )
        globals->addNamespace( ns );

    foreach( ClassModelItem item, fm->classes() )
        globals->addClass( item );

    foreach( EnumModelItem item, fm->enums() )
        globals->addEnum( item );

    foreach( FunctionDefinitionModelItem item, fm->functionDefinitions() )
        globals->addFunctionDefinition( item );

    foreach( FunctionModelItem item, fm->functions() )
        globals->addFunction( item );

    foreach( TypeAliasModelItem item, fm->typeAliases() )
        globals->addTypeAlias( item );

    foreach( VariableModelItem item, fm->variables() )
        globals->addVariable( item );

    return fm;
}

static tuple CodeModelItem_startPosition( _CodeModelItem& cm )
{
    int start, end;
    cm.getStartPosition( &start, &end );
    return make_tuple( start, end );
}

static tuple CodeModelItem_endPosition( _CodeModelItem& cm )
{
    int start, end;
    cm.getEndPosition( &start, &end );
    return make_tuple( start, end );
}

void Wrap_CodeModelItem()
{
    scope s = class_< _CodeModelItem, bases<>,
            CodeModelItem, boost::noncopyable >( "CodeModelItem", no_init )
        .add_property( "kind", &_CodeModelItem::kind )
        .add_property( "qualifiedName", &_CodeModelItem::qualifiedName )
        .add_property( "name", &_CodeModelItem::name )
        .add_property( "scope", &_CodeModelItem::scope )
        .add_property( "fileName", &_CodeModelItem::fileName )
        .add_property( "file", &_CodeModelItem::file )
        .add_property( "startPosition", &CodeModelItem_startPosition )
        .add_property( "endPosition", &CodeModelItem_endPosition )
        .add_property( "creationId", &_CodeModelItem::creationId )
        .add_property( "model",
                make_function(&_CodeModelItem::model, return_internal_reference<>()) )
        ;

    enum_< _CodeModelItem::Kind >( "Kind" )
        .value( "Scope", _CodeModelItem::Kind_Scope )
        .value( "Namespace", _CodeModelItem::Kind_Namespace )
        .value( "Member", _CodeModelItem::Kind_Member )
        .value( "Function", _CodeModelItem::Kind_Function )
        ;
}

void Wrap_TypeAliasModelItem()
{
    class_ < _TypeAliasModelItem,  bases<_CodeModelItem>, TypeAliasModelItem, boost::noncopyable>
            ("TypeAliasModelItem", no_init)
            .add_property("type", &_TypeAliasModelItem::type, &_TypeAliasModelItem::setType)
            ;
            
}

TypeInfo TypeInfo_resolveType( const TypeInfo& self, CodeModelItem scope )
{   return TypeInfo::resolveType( self, scope );
}

bool TypeInfo_eq( const TypeInfo& self, const QString& s )
{   return self.toString() == s;
}

QString TypeInfo_toString( const TypeInfo& self)
{
    QString string = self.toString();
    // the underlying parser misreports template types
    while(string.contains(">>"))
    {
        int pos = string.indexOf(">>");
        string.insert(pos+1, ' ');
    }
    if( string.isNull() )
    {
        return "<NULL>";
    }
    else
    {
        return string;
    }
}

void Wrap_CodeModel()
{
    Wrap_QHash< QString, ClassModelItem >();
    Wrap_QHash< QString, NamespaceModelItem >();
    Wrap_QHash< QString, TypeAliasModelItem >();
    Wrap_QHash< QString, VariableModelItem >();

    Wrap_QMultiHash< QString, FunctionModelItem >();
    Wrap_QMultiHash< QString, FunctionDefinitionModelItem >();

    Wrap_QList< FileModelItem >();
    Wrap_QList< NamespaceModelItem >();
    Wrap_QList< FunctionModelItem >();
    Wrap_QList< FunctionDefinitionModelItem >();
    Wrap_QList< ArgumentModelItem >();
    Wrap_QList< TemplateParameterModelItem >();
    Wrap_QList< EnumModelItem >();
    Wrap_QList< EnumeratorModelItem >();
    Wrap_QList< ClassModelItem >();
    Wrap_QList< TypeInfo >();

    Wrap_CodeModelItem();
    Wrap_TypeAliasModelItem();

    class_< Binder >( "Binder", init<CodeModel*, LocationManager&, Control*>() )
        .def( "run", &Binder::run )
        ;

    class_< TypeInfo >( "TypeInfo" )
        .add_property( "qualifiedName", &TypeInfo::qualifiedName )
        .add_property( "isConstant", &TypeInfo::isConstant )
        .add_property( "isVolatile", &TypeInfo::isVolatile )
        .add_property( "isReference", &TypeInfo::isReference )
        .add_property( "indirections", &TypeInfo::indirections )
        .add_property( "isFunctionPointer", &TypeInfo::isFunctionPointer )
        .add_property( "arrayElements", &TypeInfo::arrayElements )
        .add_property( "arguments", &TypeInfo::arguments )
        .def( "__str__", &TypeInfo_toString )
        .def( "__unicode__", &TypeInfo_toString )
        .def( "__eq__", &TypeInfo::operator== )
        .def( "__eq__", &TypeInfo_eq )

        
        .def( "resolveType", &TypeInfo_resolveType )
        ;

    {
        scope s = class_< CodeModel, boost::noncopyable >( "CodeModel" )
            .def( "parseFile", &CodeModel_parseFile )
    //        .add_property( "files", &CodeModel::files )
            .add_property( "globalNamespace", &CodeModel::globalNamespace )
            .def( "findFile", &CodeModel::findFile )
    //        .add_property( "fileMap", &CodeModel::fileMap )
            .def( "findItem", &CodeModel::findItem )
            ;

        enum_< CodeModel::AccessPolicy >( "AccessPolicy" )
            .value( "Public", CodeModel::Public )
            .value( "Private", CodeModel::Private )
            .value( "Protected", CodeModel::Protected )
            ;
    }

    class_< _ScopeModelItem, bases<_CodeModelItem>,
            ScopeModelItem, boost::noncopyable >( "ScopeModelItem", no_init )
        .def( "findClass", &_ScopeModelItem::findClass )
        .def( "findEnum", &_ScopeModelItem::findEnum )
        .def( "findFunctionDefinitions", &_ScopeModelItem::findFunctionDefinitions )
        .def( "findFunctions", &_ScopeModelItem::findFunctions )
        .def( "findTypeAlias", &_ScopeModelItem::findTypeAlias )
        .def( "findVariable", &_ScopeModelItem::findVariable )
        .add_property( "classMap", &_ScopeModelItem::classMap )
        .add_property( "enumMap", &_ScopeModelItem::enumMap )
        .add_property( "typeAliasMap", &_ScopeModelItem::typeAliasMap )
        .add_property( "variableMap", &_ScopeModelItem::variableMap )
        .add_property( "functionDefinitionMap", &_ScopeModelItem::functionDefinitionMap )
        .add_property( "functionMap", &_ScopeModelItem::functionMap )
        ;

    implicitly_convertible< ScopeModelItem, CodeModelItem >();

    class_< _ClassModelItem, bases<_ScopeModelItem>,
            ClassModelItem, boost::noncopyable >( "ClassModelItem", no_init )
        .add_property( "baseClasses", &_ClassModelItem::baseClasses )
        .add_property( "templateParameters", &_ClassModelItem::templateParameters )
        .def( "extendsClass", &_ClassModelItem::extendsClass )
        .add_property( "classType", &_ClassModelItem::classType )
        .add_property( "propertyDeclarations", &_ClassModelItem::propertyDeclarations )
        ;

    implicitly_convertible< ClassModelItem, CodeModelItem >();

    class_< _NamespaceModelItem, bases<_ScopeModelItem>,
            NamespaceModelItem, boost::noncopyable >( "NamespaceModelItem", no_init )
        .add_property( "namespaces", &_NamespaceModelItem::namespaces )
        .def( "findNamespace", &_NamespaceModelItem::findNamespace )
        .add_property( "namespaceMap", &_NamespaceModelItem::namespaceMap )
        ;

    implicitly_convertible< NamespaceModelItem, CodeModelItem >();

    class_< _FileModelItem, bases<_NamespaceModelItem>,
            FileModelItem, boost::noncopyable >( "FileModelItem", no_init )
        ;

    implicitly_convertible< FileModelItem, CodeModelItem >();

    class_< _ArgumentModelItem, bases<_CodeModelItem>,
            ArgumentModelItem, boost::noncopyable >( "ArgumentModelItem", no_init )
        .add_property( "type", &_ArgumentModelItem::type )
        .add_property( "defaultValue", &_ArgumentModelItem::defaultValue )
        .add_property( "defaultValueExpression", &_ArgumentModelItem::defaultValueExpression )
        ;

    implicitly_convertible< ArgumentModelItem, CodeModelItem >();

    class_< _MemberModelItem, bases<_CodeModelItem>,
            MemberModelItem, boost::noncopyable >( "MemberModelItem", no_init )
        .add_property( "isConstant", &_MemberModelItem::isConstant )
        .add_property( "isVolatile", &_MemberModelItem::isVolatile )
        .add_property( "isStatic", &_MemberModelItem::isStatic )
        .add_property( "isAuto", &_MemberModelItem::isAuto )
        .add_property( "isFriend", &_MemberModelItem::isFriend )
        .add_property( "isRegister", &_MemberModelItem::isRegister )
        .add_property( "isExtern", &_MemberModelItem::isExtern )
        .add_property( "isMutable",& _MemberModelItem::isMutable )
        .add_property( "accessPolicy", &_MemberModelItem::accessPolicy )
        .add_property( "templateParameters", &_MemberModelItem::templateParameters )
        .add_property( "type", &_MemberModelItem::type )
        ;

    implicitly_convertible< MemberModelItem, CodeModelItem >();

    class_< _FunctionModelItem, bases<_MemberModelItem>,
            FunctionModelItem, boost::noncopyable >( "FunctionModelItem", no_init )
        .add_property( "arguments", &_FunctionModelItem::arguments )
        .add_property( "functionType", &_FunctionModelItem::functionType )
        .add_property( "isVirtual", &_FunctionModelItem::isVirtual )
        .add_property( "isInline", &_FunctionModelItem::isInline )
        .add_property( "isExplicit", &_FunctionModelItem::isExplicit )
        .add_property( "isAbstract", &_FunctionModelItem::isAbstract )
        .add_property( "isVariadics", &_FunctionModelItem::isVariadics )
        .def( "isSimilar", &_FunctionModelItem::isSimilar )
        ;

    implicitly_convertible< FunctionModelItem, CodeModelItem >();

    class_< _FunctionDefinitionModelItem, bases<_FunctionModelItem>,
        FunctionDefinitionModelItem, boost::noncopyable>("FunctionDefinitionModelItem", no_init )
        ;
    
    implicitly_convertible< FunctionDefinitionModelItem, FunctionModelItem >();
        

    class_< _TemplateParameterModelItem, bases< _CodeModelItem >,
        TemplateParameterModelItem, boost::noncopyable >("TemplateParameterModelItem", no_init)
            ;

    implicitly_convertible< TemplateParameterModelItem, CodeModelItem >();

    class_< _EnumModelItem, bases< _CodeModelItem >, EnumModelItem, boost::noncopyable>
        ("EnumModelItem", no_init)
        .add_property("enumerators", &_EnumModelItem::enumerators)
        ;


    implicitly_convertible< EnumModelItem, CodeModelItem >();

    class_< _EnumeratorModelItem, bases< _CodeModelItem>, EnumeratorModelItem, boost::noncopyable>
        ("EnumeratorModelItem", no_init)
        .add_property("value", &_EnumeratorModelItem::value)
        ;


    implicitly_convertible< EnumeratorModelItem, CodeModelItem >();

    class_< _VariableModelItem, bases< _MemberModelItem>, VariableModelItem, boost::noncopyable>
        ("VariableModelItem", no_init)
        ;
}

